﻿using System;
using System.ComponentModel;
using System.Linq;
using VSTalk.Engine.Core.ClientManager;
using VSTalk.Engine.Core.Context;
using VSTalk.Engine.Core.EntitySession;
using VSTalk.Engine.Utils;
using VSTalk.Model;
using VSTalk.Tools;

namespace VSTalk.Engine.ViewModel.Editors.ClientEditor
{
    public class ClientEditorViewModel : INotifyPropertyChanged, IListener
    {
        private readonly Client _nativeClient;

        private ISubscription _stateUnit;
        private bool _isEditing;

        public ClientInfo Client { get; set; }

        public RelayCommand SaveChanges { get; set; }

        public RelayCommand RejectChanges { get; set; }

        private static IClientManager ClientManager
        {
            get { return AppServices.Get<IClientManager>(); }
        }

        private static ISessionModel SessionModel
        {
            get { return AppServices.Get<ISessionModel>(); }
        }

        public bool IsDisconnected
        {
            get
            {
                if (ClientContext == null)
                {
                    return true;
                }
                return ClientContext.State == ClientState.Disconnected;
            }
        }

        public bool IsEditing
        {
            get { return _isEditing; }
        }

        private IClientContext ClientContext
        {
            get { return _nativeClient == null ? null : SessionModel.GetClientContext(_nativeClient); }
        }

        public event Action Saved;

        public event Action Rejected;

        public ClientEditorViewModel()
        {
            Client = new ClientInfo();
            _isEditing = false;
            SetCommands();
        }

        public ClientEditorViewModel(Client client)
        {
            Client = new ClientInfo();
            _nativeClient = client;
            Client.Login = client.Login;
            Client.Server = client.Server;
            Client.Password = client.Password;
            _isEditing = true;
            SetCommands();
        }

        private void SetCommands()
        {
            RejectChanges = new RelayCommand(RejectChangesExecuted);
            SaveChanges = new RelayCommand(SaveChangesExecuted, SaveChangesCanExecute);
        }

        private Account Account
        {
            get { return AppServices.Get<IModelContext>().Account; }
        }

        private void RejectChangesExecuted()
        {
            OnRejected();
        }

        private void SaveChangesExecuted()
        {
            if (_nativeClient != null)
            {
                ClientManager.EditClient(_nativeClient.Id, Client);
            }
            else
            {
                ClientManager.AddClient(Client);
            }
            OnSaved();
        }

        private bool SaveChangesCanExecute()
        {
            return !string.IsNullOrEmpty(Client.Login) &&
                   !string.IsNullOrEmpty(Client.Password) &&
                   !string.IsNullOrEmpty(Client.Server);
        }

        protected virtual void OnSaved()
        {
            var handler = Saved;
            if (handler != null) handler();
        }

        protected virtual void OnRejected()
        {
            var handler = Rejected;
            if (handler != null) handler();
        }

        public void StartListen()
        {
            if (ClientContext != null)
            {
                _stateUnit = ClientContext.SubscribeToChange(() => ClientContext.State,
                        sender => PropertyChanged.Notify(() => IsDisconnected));
            }
        }

        public void StopListen()
        {
            if (_stateUnit != null)
            {
                _stateUnit.Unsubscribe();
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}